package com.gitee.qdbp.general.system.web.shiro.impl;

import java.util.Iterator;
import java.util.List;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.able.jdbc.ordering.OrderPaging;
import com.gitee.qdbp.able.jdbc.paging.Paging;
import com.gitee.qdbp.base.system.service.IPasswordService;
import com.gitee.qdbp.base.utils.SessionTools;
import com.gitee.qdbp.general.system.api.permission.model.ResourceCascadeBean;
import com.gitee.qdbp.general.system.api.permission.model.ResourceCoreBean;
import com.gitee.qdbp.general.system.api.permission.model.ResourceCoreWhere;
import com.gitee.qdbp.general.system.api.permission.service.IResourceCascadeQueryer;
import com.gitee.qdbp.general.system.api.permission.service.IResourceCoreQueryer;
import com.gitee.qdbp.general.system.api.personnel.model.UserCoreBean;
import com.gitee.qdbp.general.system.enums.SysResState;
import com.gitee.qdbp.general.system.enums.SysResType;
import com.gitee.qdbp.general.system.web.shiro.simple.LoginUser;
import com.gitee.qdbp.general.system.web.shiro.simple.ModuleOptions;
import com.gitee.qdbp.general.system.web.shiro.simple.SimpleAccountLoginRealm;
import com.gitee.qdbp.tools.files.PathTools;
import com.gitee.qdbp.tools.utils.StringTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * 管理员登录/授权
 *
 * @author zhaohuihua
 * @version 160318
 */
public class ManagerServiceRealm extends SimpleAccountLoginRealm {

    private static final Logger log = LoggerFactory.getLogger(ManagerServiceRealm.class);

    /** 登录权限的场景类型, 需要与维护权限资源界面的sceneType保持一致 **/
    private String sceneType;
    @Autowired
    private IResourceCoreQueryer resourceCoreQueryer;
    @Autowired
    private IResourceCascadeQueryer resourceCascadeQueryer;
    @Autowired
    public IPasswordService passwordService;

    public ManagerServiceRealm() {
        super();
        this.sceneType = "manager";
        this.setClearAuthorizationInfo(true);
    }

    public ManagerServiceRealm(String sceneType) {
        this.sceneType = sceneType;
    }

    /** 获取授权信息 **/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) throws AuthenticationException {
        SimpleAuthorizationInfo authorization = new SimpleAuthorizationInfo();
        LoginUser logined = (LoginUser) super.getAvailablePrincipal(principals);
        UserCoreBean user = logined.original();

        try {
            List<ResourceCoreBean> list;
            if (user.getSuperman() && "ENABLED".equals(user.getOptions(true).get("DEVELOP"))) {
                authorization.addStringPermission("*"); // 启用开发模式
                // 查询所有权限资源
                ResourceCoreWhere where = new ResourceCoreWhere();
                where.setSceneType(sceneType);
                where.setState(SysResState.NORMAL);
                OrderPaging paging = OrderPaging.of("sortIndex asc, key asc");
                list = resourceCoreQueryer.list(where, paging).toList();
            } else {
                // 查询登录用户所拥有的权限
                ResourceCoreWhere where = new ResourceCoreWhere();
                where.setSceneType(sceneType);
                where.setState(SysResState.NORMAL);
                // ORDER BY `res_type` ASC, `sort_index` ASC, `res_key` ASC
                OrderPaging paging = OrderPaging.of(Paging.NONE, "type ASC, sortIndex ASC, key ASC");
                list = resourceCoreQueryer.listByUserId(logined.getId(), where, paging, true);
                if (VerifyTools.isBlank(list)) {
                    // 查询当前用户类型的默认角色
                    list = resourceCoreQueryer.listByUserType(user.getUserType(), sceneType, true);
                }
            }

            for (ResourceCoreBean item : list) {
                if (item.getType() == SysResType.OPERATE) {
                    authorization.addStringPermission(item.getKey());
                } else {
                    String prefix = item.getType().name() + ":";
                    authorization.addStringPermission(prefix + item.getKey());
                }
            }
            ResourceCascadeBean root = resourceCascadeQueryer.wrap(list);

            // 整理权限资源
            Iterator<ResourceCascadeBean> itr = root.breadthFirstIterator();
            while (itr.hasNext()) {
                ResourceCascadeBean item = itr.next();
                if (item.getType() == SysResType.MODULE) {
                    ModuleOptions options = handleModuleOptions(item);
                    // 改为深度优先遍历
                    // 广度优化遍历存在BUG: 如果菜单组和菜单并列, firstMenu取到的是外层的菜单而不是第1个菜单组下面的菜单
                    ResourceCascadeBean firstMenu = findFirstMenu(item);
                    if (firstMenu != null) {
                        options.setSublink(PathTools.concat(item.getUrl(), firstMenu.getUrl()));
                    }
                }

            }

            // 记录到session中
            SessionTools.setUserResources(root);
        } catch (ServiceException e) {
            throw new AuthenticationException("Failed to authentication authority.", e);
        }

        log.trace("Successful to authorization: {}", user);

        try {
            onAuthorizeSuccess(logined);
        } catch (ServiceException e) {
            throw new AuthenticationException("Failed to check authentication login.", e);
        }
        return authorization;
    }

    private ResourceCascadeBean findFirstMenu(ResourceCascadeBean root) {
        Iterator<ResourceCascadeBean> itr = root.depthFirstIterator();
        while (itr.hasNext()) {
            ResourceCascadeBean item = itr.next();
            if (item.getType() == SysResType.MENU) {
                return item;
            }
        }
        return null;
    }

    private ModuleOptions handleModuleOptions(ResourceCascadeBean item) {
        ModuleOptions options;
        if (item.getOptions() == null) {
            options = new ModuleOptions();
        } else {
            options = item.getOptions().to(ModuleOptions.class);
        }
        if (StringTools.isUrl(item.getUrl())) {
            options.setAbsolute(true);
        }
        item.setOptions(options);
        return options;
    }
}
